/*
 *  fmfApp.cpp 
 *
 *  openFrameworks
 *
 *  Created by lucas bigeardel on 16/02/10.
 *  Copyright Lucas Bigeardel 2010. All rights reserved.
 *  This file is part of Fmurf.
 *
 *  Fmurf is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Fmurf is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Fmurf.  If not, see <http://www.gnu.org/licenses/>
 *
 */

#include "stdio.h"
#include "Poco/Delegate.h"
#include "Poco/Timestamp.h"
#include "smuon.h"
#include "brain.h"
#include "surface.h"
#include "x3nityApp.h"




//--------------------------------------------------------------
void x3nityApp::setup(){
	
	ofEnableSmoothing(); 
	
	smurfFloor = new Smurfloor(60, 5);
	smurfCfg = new SmurfCfg();
	sender = new ofxOscSender();
	receiver = new ofxOscReceiver();
	
	synthUtils = new FMFUtils();
	
	fontMap = new map<string, ofTrueTypeFont*>();
	
	vector<FontCfg*> *fontNames = new vector<FontCfg*>();
	
	fontNames->push_back(new FontCfg("ARCADE_R.ttf", 12));
	fontNames->push_back(new FontCfg("ANGERTH.TTF", 36));
	fontNames->push_back(new FontCfg("aelfa.TTF", 36));
	fontNames->push_back(new FontCfg("FanjLeod.TTF", 36));
	fontNames->push_back(new FontCfg("Fanjofey.ttf", 36));
	fontNames->push_back(new FontCfg("firstv2.ttf", 36));
	fontNames->push_back(new FontCfg("Odinson.ttf", 36));
	fontNames->push_back(new FontCfg("rune.ttf", 36));
	fontNames->push_back(new FontCfg("TENGWAR.TTF", 36));
	
    for (int i=0; i<fontNames->size(); i++) {
		ofTrueTypeFont *f = new ofTrueTypeFont();
		f->loadFont(fontNames->at(i)->fontName, fontNames->at(i)->fontSize);
		fontMap->insert(pair<string, ofTrueTypeFont*>(fontNames->at(i)->fontName, f));
	}
	
	ofAddListener(tuio.objectAdded,smurfFloor->brain,&SmurfBrain::objectAdded);
	ofAddListener(tuio.objectRemoved,smurfFloor->brain,&SmurfBrain::objectRemoved);
	ofAddListener(tuio.objectUpdated,smurfFloor->brain,&SmurfBrain::objectUpdated);
	ofAddListener(tuio.cursorAdded,smurfFloor->brain,&SmurfBrain::tuioAdded);
	ofAddListener(tuio.cursorRemoved,smurfFloor->brain,&SmurfBrain::tuioRemoved);
	ofAddListener(tuio.cursorUpdated,smurfFloor->brain,&SmurfBrain::tuioUpdated);
		
	tuio.start(smurfCfg->getTuioPort());
	
	ofSetLogLevel(OF_LOG_VERBOSE);
	
	ofBackground(77.0, 77.0, 77.0);
	ofSetVerticalSync(false);
	
	
	windowResized(ofGetWidth(), ofGetHeight());		// force this at start (cos I don't think it is called)
	
	
	//---------------------------------------------------
	
	sender->setup(smurfCfg->getOscSendAddress(), smurfCfg->getOscSendPort() );
	receiver->setup(smurfCfg->getOscReceivePort());
	
	smurfFloor->setOscSender(sender);
	smurfFloor->setOscReceiver(receiver);
	smurfFloor->setSmurfCfg(smurfCfg);
	smurfFloor->brain->loadGestures();
	
}

void x3nityApp::handleAnchorsUpdate(SConnection *_connection) {
	if (_connection == NULL) return;
	
	if (_connection->fromAnchor() != NULL && _connection->toAnchor() != NULL) {
	
		SmurfAnchor *fromAnchor = _connection->fromAnchor();
		
		if (fromAnchor == NULL) return;
		
		float fSid = fromAnchor->getOwnerSid();
		
		if (fSid == -FLT_MAX) return;
	
		Smurf *from = smurfFloor->brain->findSmurf(fSid);
		
		if (from == NULL || from->getTuio() == NULL) return;
		
		ofxTuioPoint *fromPt = new ofxTuioPoint(
							   from->getTuio()->getPosition().getX(), 
							   from->getTuio()->getPosition().getY()
						   );

		
		SmurfAnchor *toAnchor = _connection->toAnchor();
		
		if (toAnchor == NULL) return;

		float tSid = toAnchor->getOwnerSid();
		
		if (tSid == -FLT_MAX) return;
		
		Smurf *to = smurfFloor->brain->findSmurf(tSid);

		if (to == NULL || to->getTuio() == NULL) return;

		ofxTuioPoint *toPt = new ofxTuioPoint(
							   to->getTuio()->getPosition().getX(),
							   to->getTuio()->getPosition().getY()
						   );
		
		
		

		float angle = PI-fromPt->getAngle(toPt);
	
		((ofxTuioObject*)fromAnchor->getTuio())->update(
								  fromPt->getX()+cosf(angle)*((from->getRadius()+1)/ofGetWidth()), 
								  fromPt->getY()+sinf(angle)*((from->getRadius()+1)/ofGetHeight()), 
								  ((ofxTuioObject*)fromAnchor->getTuio())->getAngle(), 
								  fromAnchor->getTuio()->getXSpeed(), 
								  fromAnchor->getTuio()->getYSpeed(), 
								  0.0f, 
								  fromAnchor->getTuio()->getMotionSpeed(), 
								  fromAnchor->getTuio()->getMotionAccel()
						);
	
		((ofxTuioObject*)toAnchor->getTuio())->update(
								  toPt->getX()-cosf(angle)*((to->getRadius()+1)/ofGetWidth()), 
								  toPt->getY()-sinf(angle)*((to->getRadius()+1)/ofGetHeight()), 
								  ((ofxTuioObject*)toAnchor->getTuio())->getAngle(), 
								  toAnchor->getTuio()->getXSpeed(), 
								  toAnchor->getTuio()->getYSpeed(), 
								  0.0f, 
								  toAnchor->getTuio()->getMotionSpeed(), 
								  toAnchor->getTuio()->getMotionAccel()
						);
	
		delete fromPt;
		delete toPt;
	}
}

//--------------------------------------------------------------
void x3nityApp::update(){
	ofBackground(76, 76, 76);
	
	smurfFloor->think();
	
	tuio.getMessage();
	
	///------------------------------------------------------
	
	
	windowResized(ofGetWidth(), ofGetHeight());		// force this at start (cos I don't think it is called)
	
//	// do finger stuff
//	list<ofxTuioCursor*>cursorList = tuio.getTuioCursors();
//	for(list<ofxTuioCursor*>::iterator it=cursorList.begin(); it != cursorList.end(); it++) {
//		ofxTuioCursor *tcur = (*it);
//        float vx = tcur->getXSpeed() * tuioCursorSpeedMult;
//        float vy = tcur->getYSpeed() * tuioCursorSpeedMult;
//        if(vx == 0 && vy == 0) {
//            vx = ofRandom(-tuioStationaryForce, tuioStationaryForce);
//            vy = ofRandom(-tuioStationaryForce, tuioStationaryForce);
//        }
//        addToFluid(tcur->getX(), tcur->getY(), vx, vy, true);
//    }
	
//	for (int i=0; i<smurfFloor->brain->getSmurfs()->size(); i++ ) {
//		Smurf* s = smurfFloor->brain->getSmurfs()->at(i);
//		s->start();
//		ofxTuioObject *blob = (ofxTuioObject*) s->getTuio();
//		//addToFluid(blob->getX(), blob->getY(), blob->getMotionSpeed(), blob->getMotionSpeed(), false);
//		addToFluid(blob->getX(), blob->getY(), blob->getRotationAccel(), blob->getRotationAccel(), true);
//	}
	
	for (int i=0; i<smurfFloor->brain->getConnections()->size(); i++ ) {
		SConnection *connection = smurfFloor->brain->getConnections()->at(i);
		if (connection != NULL) {
			handleAnchorsUpdate(connection);
		}
	}
	
//	for (int i=0; i<smurfFloor->waves->size(); i++ ) {
//		SWave* w = smurfFloor->waves->at(i);
//				
//		if (w->getFid() != -1) {
//			for (float cc=0.0;cc<15.0;cc++) {
//				float vx = ofRandom(-tuioStationaryForce, tuioStationaryForce);
//				float vy = ofRandom(-tuioStationaryForce, tuioStationaryForce);
//			
//				float x = (w->getX() + cosf(ofDegToRad(cc*24.0))*w->getRadius())/ofGetWidth();
//				float y = (w->getY() + sinf(ofDegToRad(cc*24.0))*w->getRadius())/ofGetHeight();
//				
//				//Smurf *s = smurfFloor->brain->findSmurf(w->getFid());
//				//if (s != NULL && w->getX(),w->getRadius()
//					addToFluid(
//							x,
//							y,
//							0.001,
//							0.001,
//							true
//							);
//			}
//		}
//	}
//		
//	fluidSolver.update();
	
	// save old mouse position (openFrameworks doesn't do this automatically like processing does)
	pmouseX = mouseX;
	pmouseY = mouseY;
	
}

//--------------------------------------------------------------
void x3nityApp::draw(){
	ofNoFill();

	ofSetColor(0, 0, 0);

	//drawWaves();
	drawConnections();
	drawSmurfs();
	drawWindows();
	drawFingers();
		
//	gui.draw();
	
	if (smurfFloor->isVerbose()) {
//		ofDrawBitmapString(
//						   "BPM     : " + 
//						   ofToString((int)smurfFloor->getBPM()), 
//						   15, 45
//		);

		ofDrawBitmapString(
						   "OSC out : " + 
						   smurfCfg->getOscSendAddress() + 
						   ":" + 
						   ofToString((int)smurfCfg->getOscSendPort()),
						   15, 60
		);
		
		ofDrawBitmapString(
						   "OSC in  : " + 
						   ofToString((int)smurfCfg->getOscReceivePort()), 
						   15, 75
		);
		
		ofDrawBitmapString(
						   "TUIO    : " + 
						   ofToString((int)smurfCfg->getTuioPort()), 
						   15, 90
		);
		
//		ofDrawBitmapString("      q : quicken the beat", 15, 105);
//		ofDrawBitmapString("      s : slow down the beat", 15, 120);
		ofDrawBitmapString("      f : toggle fullscreen", 15, 135);
//		ofDrawBitmapString("  space : ui menu", 15, 150);
		ofDrawBitmapString("    esc : quit", 15, 165);
		
#ifdef TARGET_OS_MAC
		string gestMode = smurfFloor->isGesture()?"ON":"OFF";
		ofDrawBitmapString("      g : toggle gesture mode (" + gestMode + ")", 15, 180);
#endif
		ofDrawBitmapString("      h : toggle this help menu", 15, 195);

		string connMode = smurfFloor->isConnectionModeOn()?"ON":"OFF";
		ofDrawBitmapString("      c : toggle connections mode(" + connMode + ")", 15, 210);
//		ofDrawBitmapString("      x : quicken the wave", 15, 225);
//		ofDrawBitmapString("      w : slow down the wave", 15, 240);

#ifdef TARGET_OS_MAC
		ofDrawBitmapString("Gesture : " + smurfFloor->brain->getGestures()->getLatestGestureLabel(), 15, 255);
#endif
		
//		float fps = ofGetFrameRate();
//		
//		ofDrawBitmapString("FPS : " + (int)fps, 5, 65);
	}
}


void x3nityApp::windowResized(int w, int h) {
	window.width		= w;
	window.height		= h;
	
	window.invWidth		= 1.0f/window.width;
	window.invHeight	= 1.0f/window.height;
	window.aspectRatio	= window.width * window.invHeight;
	window.aspectRatio2 = window.aspectRatio * window.aspectRatio;
	
//	resizeFluid = true;
}

//--------------------------------------------------------------




void x3nityApp::smurfCollisionComputation(SWave *wave) {
}

//
//--------------------------------------------------------------
//
//void fmfApp::drawWaves() {
//	vector<SWave*> *tmpWaves = new vector<SWave *>();
//	for (int i=0; i<smurfFloor->waves->size(); i++ ) {
//		SWave* w = smurfFloor->waves->at(i);
//	
//		w->setRadius(w->getRadius()+smurfFloor->getBPM()/20+smurfFloor->getWaveSpeed()/5);
//	
//		if (w->getFid() == -1) {
//			//w->draw();		
//		}
//		
//		if (w->getRadius() > w->getRange()) {
//			smurfFloor->waves->erase(smurfFloor->waves->begin()+i);
//		} else {
//			for (int i=0; i<smurfFloor->brain->getSmurfs()->size(); i++ ) {
//				Smurf* s = smurfFloor->brain->getSmurfs()->at(i);
//				if (s != NULL) {
//					ofxTuioObject *blob = (ofxTuioObject*) s->getTuio();
//				
//					if (dynamic_cast<SmurfAnchor*>(s) == NULL) {
//
//						float dist = ofDist(blob->getX()*ofGetWidth(), blob->getY()*ofGetHeight(), w->getX(), w->getY());
//						if (w->getFid() != blob->getFiducialId() && dist  < w->getRadius()) {
//							if (s->getLastWaveUid() < w->getUid()) {
//								s->setLastWaveUid(w->getUid());
//												
//								int id = smurfFloor->getNextWaveUid();
//							
//								s->onBeat(sender, w);
//								tmpWaves->push_back(
//												new SWave(
//														  blob->getFiducialId(), // emmiter
//														  id, // uid
//														  blob->getX()*ofGetWidth(), // x pos
//														  blob->getY()*ofGetHeight(), // y pos
//														  20
//												)
//								);
//							}
//						}
//					}
//				}
//			}
//		}
//	}	
//	for (int i=0; i<tmpWaves->size(); i++ ) {
//		smurfFloor->waves->push_back(tmpWaves->at(i));
//	}
//	delete tmpWaves;
//}

void x3nityApp::drawWindows() {
	for (int i=0; i<smurfFloor->brain->getUIManager()->getWindows()->size(); i++ ) {
		SWindow *w = smurfFloor->brain->getUIManager()->getWindows()->at(i);
		if (w != NULL) {
			w->draw(fontMap, "ARCADE_R.ttf");
		}
	}	
}

void x3nityApp::drawSmurfs() {
	for (int i=0; i<smurfFloor->brain->getSmurfs()->size(); i++ ) {
		Smurf *smurf = smurfFloor->brain->getSmurfs()->at(i);
		if(smurf->tic() == 1 && smurf->getKind() != SMURF_KIND_ANCHOR && smurf->getKind() != SMURF_KIND_HANDLE) {
			smurf->onTic(smurfFloor->brain->getOscSender());
		}
		if (dynamic_cast<SmurfAnchor*>(smurf) != NULL) {
			smurf->draw();
		} else {
			smurf->draw(fontMap, "rune.ttf");
		}
	}	
}

void x3nityApp::drawConnections() {
	vector<SConnection*> *connections = smurfFloor->brain->getConnections();
	for (vector<SConnection*>::iterator it=connections->begin();it != connections->end(); it++) {
		SConnection *connection = *it;
		Smurf * from = smurfFloor->brain->findSmurf(connection->fromSID());
		Smurf * to = smurfFloor->brain->findSmurf(connection->toSID());
		if (from != NULL && to != NULL) {
			if (from->getTuio() != NULL && to->getTuio() != NULL) {
				if (from->getLiveData() != NULL) {
					connection->draw(from->getLiveData());
				}
			}
		}
	}	
}

void x3nityApp::drawFingers() {
	for (int i=0; i<smurfFloor->brain->getFingers()->size(); i++ ) {
		SmurFinger* f = smurfFloor->brain->getFingers()->at(i);
		f->draw(smurfFloor->isGesture());
	}	
}



//--------------------------------------------------------------


#pragma mark Input callbacks

//--------------------------------------------------------------
void x3nityApp::keyPressed  (int key){ 
    switch(key) {
//		case ' ':
//			gui.toggleDraw();	
//			glClear(GL_COLOR_BUFFER_BIT);
//			break;			
		case 'f':
			ofToggleFullscreen();
			break;
		case 'p':
			static char fileNameStr[255];
			sprintf(fileNameStr, "output_%0.4i.png", ofGetFrameNum());
			static ofImage imgScreen;
			imgScreen.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
			printf("Saving file: %s\n", fileNameStr);
			imgScreen.saveImage(fileNameStr);
			break;
		case 'a':
			if (smurfFloor->getBPM() <= 299) {
				// quicken the beat
				// In my case just got the griffin knob linked to this
				smurfFloor->setBPM(smurfFloor->getBPM()+1);
			}
			break;
		case 'z':
			if (smurfFloor->getBPM() > 1) {
				// slow down the beat
				// In my case just got the griffin knob linked to this
				smurfFloor->setBPM(smurfFloor->getBPM()-1);
			}
			break;
		case 'h': // help
			smurfFloor->setVerbose(smurfFloor->isVerbose()?false:true);
			break;
#ifdef TARGET_OS_MAC
		case 'g': // gesture
			smurfFloor->toggleGesture();
			break;
#endif
		case 'c': // gesture
			smurfFloor->toggleConnectionMode();
			break;
//		case 'w': // wave
//			if (smurfFloor->getWaveSpeed() > 1) {
//				smurfFloor->setWaveSpeed(smurfFloor->getWaveSpeed()-1);
//			}
//			break;
//		case 'x': // wave
//			if (smurfFloor->getWaveSpeed() <= 20) {
//				smurfFloor->setWaveSpeed(smurfFloor->getWaveSpeed()+1);
//			}
//			break;
    }
}

//--------------------------------------------------------------
void x3nityApp::keyReleased(int key){

}


//
// Simulate a TUIO cursor here !!!!
//

//--------------------------------------------------------------
void x3nityApp::mouseMoved(int x, int y ){
//	float mouseNormX = x * window.invWidth;
//    float mouseNormY = y * window.invHeight;
//    float mouseVelX = (x - pmouseX) * window.invWidth;
//    float mouseVelY = (y - pmouseY) * window.invHeight;
//	
}

void x3nityApp::mouseDragged(int x, int y, int button) {
//	float mouseNormX = x * window.invWidth;
//    float mouseNormY = y * window.invHeight;
//    float mouseVelX = (x - pmouseX) * window.invWidth;
//    float mouseVelY = (y - pmouseY) * window.invHeight;
//	
}

//--------------------------------------------------------------
void x3nityApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void x3nityApp::mouseReleased(int x, int y, int button){

}



